package com.zj.baseproxy.config;

import com.zj.baseproxy.interfaces.BeanCreateRule;
import com.zj.baseproxy.service.BaseInterface;
import com.zj.baseproxy.service.impl.BaseInterfaceImpl;
import com.zj.baseproxy.template.BaseTemplate;
import com.zj.baseproxy.template.Getter;
import com.zj.baseproxy.template.handler.BaseTemplateImpl;
import com.zj.baseproxy.utils.ClassHelper;
import lombok.RequiredArgsConstructor;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.NamingStrategy;
import net.bytebuddy.description.modifier.FieldManifestation;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FieldAccessor;
import net.bytebuddy.implementation.InvocationHandlerAdapter;
import net.bytebuddy.implementation.MethodCall;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 自定义配置
 */
@Configuration
@RequiredArgsConstructor
public class CustomConfig {

    private static final Logger log = LoggerFactory.getLogger(CustomConfig.class);

    @Bean
    public BeanCreateRule beanCreateRule(ClassHelper classHelper, BaseTemplateImpl baseTemplateImpl, BaseInterfaceImpl baseInterface) {
        return new BeanCreateRule() {
            @Override
            public <T> T create(Class<T> clazz) {
                // 使用byte-buddy动态代理接口
                Object o = null;
                try {
                    Class<?>[] interfaces = clazz.getInterfaces();
                    DynamicType.Builder.MethodDefinition.ReceiverTypeDefinition<Object> receiverTypeDefinition = new ByteBuddy().with(new NamingStrategy.AbstractBase() {
                        @Override
                        protected String name(TypeDescription typeDescription) {
                            return "com.zj.baseproxy.template.service.impl." + clazz.getSimpleName() + "Impl";
                        }
                    })
                            .subclass(Object.class)
                            .implement(clazz)
                            // 自定义方法用自定义拦截
                            .method(ElementMatchers.isDeclaredBy(clazz))
                            .intercept(InvocationHandlerAdapter.of((o1, method, objects) -> null))
                            .method(ElementMatchers.isDeclaredBy(BaseInterface.class))
                            .intercept(MethodDelegation.to(baseInterface));
                    // 拦截通用模板方法
                    for (Class<?> anInterface : interfaces) {
                        if (BaseTemplate.class == anInterface) {
                            // 定义一个属性private final Class typeClz; 为BaseTemplate的泛型class
                            receiverTypeDefinition = receiverTypeDefinition.defineField("typeClz", Class.class, Visibility.PRIVATE, FieldManifestation.FINAL)
                                    // 通用模板方法用通用拦截
                                    .method(ElementMatchers.isDeclaredBy(BaseTemplate.class))
                                    .intercept(MethodDelegation.to(baseTemplateImpl))
                                    // 定义一个属性private final String dmsUrl;
                                    .defineField("dmsUrl", String.class, Visibility.PRIVATE, FieldManifestation.FINAL)
                                    // 覆写构造方法,赋值final属性
                                    .constructor(ElementMatchers.isDefaultConstructor())
                                    .intercept(MethodCall.invoke(Object.class.getDeclaredConstructor())
                                            .andThen(FieldAccessor.ofField("typeClz").setsValue(ClassHelper.getBaseTemplateTypeClass((Class<? extends BaseTemplate<?>>) clazz)))
                                            .andThen(FieldAccessor.ofField("dmsUrl").setsValue(classHelper.getDmsUrl((Class<? extends BaseTemplate<?>>) clazz)))
                                    )
                                    // 拦截get方法
                                    .method(ElementMatchers.isDeclaredBy(Getter.class))
                                    .intercept(FieldAccessor.ofBeanProperty());
                        }
                    }

                    o = receiverTypeDefinition.make()
                            .load(clazz.getClassLoader())
                            .getLoaded()
                            .newInstance();
                } catch (InstantiationException | IllegalAccessException | NoSuchMethodException e) {
                    log.warn("bean创建失败", e);
                }
                return (T) o;
            }
        };
    }
}
